#ifndef __UNP_H
#define __UNP_h

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <stdarg.h>
#include <mcheck.h>
#include <malloc.h>
#include <locale.h>
#include <assert.h>
#include <error.h>
#include <math.h>
#include <getopt.h>

#include <unistd.h>
#include <limits.h>
#include <fcntl.h>
#include <pwd.h>
#include <poll.h>
#include <sys/epoll.h>
#include <signal.h>
#include <errno.h>
#include <curses.h>
#include <termios.h>
#include <semaphore.h>
#include <glob.h>
#include <regex.h>
#include <fnmatch.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/utsname.h>
#include <sys/select.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#include<sys/types.h>
#include<sys/socket.h>
#include<sys/time.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<sys/uio.h>
#include<sys/un.h>
#include<sys/select.h>

#define _GNU_SOURCE
#define __USE_GNU
//#define __GNUC__

#define offset(t,m) (int)&(((t*)0)->m) 
#define  SA  struct sockaddr
#define  BUFSIZE 8192
#define  FILE_MODE 0644
#define  DIR_MODE 0755
#define  OPEN_MAX 1024

#define open_max()\
	sysconf(_SC_OPEN_MAX)


#define max(a,b) (a)>(b)?(a):(b)
#define min(a,b) (a)<(b)?(a):(b)

#define swap(a,b)                               \
	do{                                         \
		typeof(a) t=a;                          \
		a=b;                                    \
		b=t;                                    \
	}while(0)

#define strdupa(s)                                                            \
	(__extension__                                                            \
	 ({                                                                       \
	  __const char *__old = (s);                                              \
	  size_t __len = strlen (__old) + 1;                                      \
	  char *__new = (char *) __builtin_alloca (__len);                        \
	  (char *) memcpy (__new, __old, __len);                                  \
	  }))


void err_sys(const char *fmt, ...);
void err_quit(const char *fmt, ...);
void err_ret(const char *fmt, ...);
void *xmalloc(size_t sz);
int connect_sock(const char *host, const char *service, const char *transport);
int connect_tcp(const char *host, const char *service);
int connect_udp(const char *host, const char *service);


/***********************************************************************/
/*this is APUE style error process function, Not safe in thread.(exit)   */
/***********************************************************************/
void err_sys(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	fprintf(stderr, ": %s\n", strerror(errno));
	va_end(ap);

	exit(1);
}

void err_quit(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	fputs("\n", stderr);
	va_end(ap);
	exit(1);
}

void err_ret(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	fputs("\n", stderr);
	va_end(ap);
}

/********************************************************************************/
/*          coreutils style                                                     */
/********************************************************************************/
void *xmalloc(size_t sz)
{
	void *p;
	p=malloc(sz);
	if(!p)
	{
		fprintf(stderr,"memory exhausted.\n");
		exit(EXIT_FAILURE);
	}

	return p;
}

pid_t Fork()
{
	pid_t pid=fork();
	if(pid==-1)
		err_sys("fork");

	return pid;
}

/**********************************************************************/
/* Wrappers for UNIX IO */
/**********************************************************************/
int Open(const char *path, int oflag, ... )
{
	int fd;
	va_list ap;

	va_start(ap,oflag);
	fd=open(path,oflag,ap);
	va_end(ap);

	if(fd==-1)
		err_sys("open");

	return fd;
}

int Close(int fd)
{
	if( close(fd) == -1)
		err_sys("close");

	return 0;
}

ssize_t Read(int fd, char *buf, size_t len)
{
	ssize_t sz;
	while( (sz=read(fd,buf,len))==-1 && errno == EINTR)
	{}

	if(sz==-1)
		err_sys("read");

	return sz;
}

ssize_t Write(int fd, char *buf, size_t  len)
{
	ssize_t sz;
	while( (sz=write(fd,buf,len)) != len && errno==EINTR)
	{}

	if(sz != len)
		err_sys("write");

	return len;
}

off_t Lseek(int fd, off_t offset, int where)
{
	off_t off = lseek(fd,offset,where);
	if(off<0)
		err_sys("lseek");

	return off;
}


int Select(int maxfd, fd_set *rdset, fd_set *wrset,
		fd_set *exset, struct timeval *tv)
{
	int ret=select(maxfd, rdset, wrset, exset, tv);
	if(ret<0)
		err_sys("select");

	return ret;
}

int Dup2 (int o, int n)
{
	int  rc=dup2(o,n);
	if(rc<0)
		err_sys("dup2");

	return rc;
}


void * Malloc(size_t size)
{
	void *p=malloc(size);
	if(!p)
		err_quit("memory exhausted");

	return p;
}


int Socket(int domain, int type, int protocal)
{
	int sock=socket(domain, type, protocal);
	if(sock == -1)
		err_sys("socket");

	return sock;
}

int Bind(int socket, void *address, socklen_t address_len)
{
	int ret;
	ret=bind(socket, (SA*)address, address_len);
	if(ret<0)
		err_sys("bind");

	return ret;
}

int Connect(int socket, void *address, socklen_t address_len)
{
	int ret=connect(socket, (SA*)address, address_len);
	if(ret<0)
		err_sys("connect");

	return ret;
}

int Listen(int sock, int backlog)
{
	if ( listen(sock, backlog) < 0)
		err_sys("listen");

	return  0;
}

int Accept(int sock, void *address, socklen_t *len)
{
	int ret=accept(sock,(SA*)address, len);
	if(ret<0)
		err_sys("accept");

	return ret;
}


/***********************************************************************/
/*              connect_sock                                           */
/***********************************************************************/
int connect_sock(const char *host, const char *service, const char *transport)
{
	struct hostent *phe;
	struct servent *pse;
	struct sockaddr_in  addr;
	int fd, type;

	memset(&addr,0,sizeof(addr) );
	addr.sin_family = AF_INET;

	pse = getservbyname(service, transport);
	if(pse)
		addr.sin_port = pse->s_port;
	else
	{
		addr.sin_port = htons( (unsigned short)atoi(service));
		if( !addr.sin_port )
			err_quit("Cann't get %s service entry", service);
	}


	phe = gethostbyname(host);
	if(phe)
		memcpy(&addr.sin_addr, *phe->h_addr_list, sizeof( addr.sin_addr ));
	else
	{
		addr.sin_addr.s_addr = inet_addr(host);
		if(addr.sin_addr.s_addr == INADDR_NONE)
			err_quit("Cann't get %s host entry", host);
	}

	if(strcmp(transport, "udp")==0)
		type=SOCK_DGRAM;
	else if( strcmp(transport,"tcp")==0)
		type=SOCK_STREAM;

	fd=Socket(PF_INET,type,0);
	Connect(fd,&addr,sizeof(addr));

	return fd;
}

int connect_tcp(const char *host, const char *service)
{
	return connect_sock(host,service,"tcp");
}

int connect_udp(const char *host, const char *service)
{
	return connect_sock(host,service,"udp");
}

/*******************************************************/
/*           passive_sock                               */
/*******************************************************/
int passive_sock(const char *service, const char *transport, int qlen)
{
	struct servent *pse;
	struct sockaddr_in sin;
	int s,type;

	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr=INADDR_ANY;

	pse=getservbyname( service, transport );
	if(pse)
		sin.sin_port=pse->s_port;
	else
	{
		if( (sin.sin_port=htons( (unsigned short)atoi(service))) == 0)
			err_quit("Cann't get %s service entry", service);
	}

	if(strcmp(transport,"tcp")==0)
		type = SOCK_STREAM;
	else if( strcmp(transport, "udp") == 0)
		type = SOCK_DGRAM;

	s=Socket(PF_INET,type,0);
	Bind(s,&sin, sizeof(sin));

	if( type==SOCK_STREAM )
		Listen(s,qlen);

	return s;    
}

int passive_tcp(const char *service,int qlen)
{
	return passive_sock(service,"tcp",qlen);
}

int passive_udp(const char *service,int qlen)
{
	return passive_sock(service,"udp",qlen);
}


/*********************************************/
//  ReadLine Readn Writen
/*********************************************/
char *readline(int fd,char *buf, size_t sz)
{
	int ch,ret;
	char *p=buf, *q=buf+sz-2;
	if(buf==NULL || sz<3)
		err_quit("jreadline buf is NULL");

	while(p!=q){
		ret=Read(fd,(char*)&ch,1);
		if(ret==0){
			if(p==buf)
				return NULL;
			break;
		}else{
			if(ch=='\n')
				break;
			*p++=ch;
		}
	}

	*p++='\n';
	*p='\0';
	return buf;
}

/*readn*/
ssize_t readn(int fd, char *buf, size_t sz)
{
	size_t left=sz,len;
	char *p=buf;

	if(buf==NULL)
		err_quit("buf is NULL");

	while( (len=Read(fd,p,left))>0){
		p += len;
		left -= len;
		if( !left )
			break;
	}

	return  (sz-left);
}

/**writen*/
ssize_t writen(int fd, char *buf, size_t sz)
{
	char *p=buf;
	size_t left=sz,len;

	while(left>0)
	{
		while( (len=write(fd,p,left))==-1 && errno==EINTR )
		{}

		if(len == -1)
			err_sys("write");

		p += len;
		left -= len;
	}

	return sz;
}

/*********************************************/
// connect_un and passive_un
/*********************************************/
int connect_un(char *path)
{
	size_t len;
	int fd;

	struct sockaddr_un sun;
	sun.sun_family = AF_UNIX;
	strncpy( sun.sun_path, path, 108 );
	len=sizeof(sun.sun_family)+strlen(sun.sun_path);
	fd=Socket(AF_UNIX,SOCK_STREAM,0);
	Connect(fd,&sun,len);

	return fd;
}

int passive_un(char *path)
{
	size_t len;
	int fd;

	struct sockaddr_un sun;
	sun.sun_family = AF_UNIX;
	strncpy( sun.sun_path, path, 108 );
	len=sizeof(sun.sun_family)+strlen(sun.sun_path);
	unlink(path);
	fd=Socket(AF_UNIX,SOCK_STREAM,0);
	Bind(fd,&sun,len);
	Listen(fd,0);

	return fd;
}

int jconnect_sock(const char *host,const  char *service, const char *sock)
{

	struct addrinfo hints, *addr;

	memset( &hints, 0, sizeof(hints) );
	hints.ai_flags= AI_ADDRCONFIG;

	if(strcmp(sock, "tcp") == 0 )
		hints.ai_socktype = SOCK_STREAM;
	else if( strcmp(sock, "udp") == 0)
		hints.ai_socktype = SOCK_DGRAM;
	else 
		err_quit("unknow socktype");

	if( getaddrinfo(host,service, &hints, &addr) )
		err_sys("getaddrinfo");

	if(!addr)
		err_quit("Cann't get addrinfo");

	int fd = Socket(AF_INET,addr->ai_socktype, addr->ai_protocol );
	struct sockaddr_in *pin = (void*)addr->ai_addr;

	Connect(fd, pin, sizeof(*pin));

	freeaddrinfo(addr);

	return fd;
}

int jconnect_tcp(char *host, char *service)
{
	return jconnect_sock(host, service, "tcp");
}

int jconnect_udp(char *host, char *service)
{
	return jconnect_sock(host, service, "udp");
}

int jpassive_sock(char *service,int backlog, char* socktype)
{
	struct addrinfo hints, *addr;
	char *host="localhost";

	memset( &hints, 0, sizeof(hints) );
	hints.ai_flags= AI_ADDRCONFIG;

	if(strcmp(socktype, "tcp") == 0 )
		hints.ai_socktype = SOCK_STREAM;
	else if( strcmp(socktype, "udp") == 0)
		hints.ai_socktype = SOCK_DGRAM;
	else 
		err_quit("unknow socktype");

	if( getaddrinfo(host,service, &hints, &addr) )
		err_sys("getaddrinfo");

	if(!addr)
		err_quit("Cann't get addrinfo");

	int fd = Socket(AF_INET,addr->ai_socktype, 0 );
	struct sockaddr_in sin;
	memset(&sin, 0,sizeof(sin));
	sin.sin_family=AF_INET;
	sin.sin_addr.s_addr=htonl(INADDR_ANY);
	sin.sin_port = ((struct sockaddr_in *)addr->ai_addr)->sin_port;

	Bind(fd, &sin, sizeof(sin) );

	if(hints.ai_socktype == SOCK_STREAM)
		Listen(fd, backlog);

	freeaddrinfo(addr);

	return fd;
}

int jpassive_tcp(char *service,int backlog)
{
	return jpassive_sock(service, backlog, "tcp");
}

int jpassive_udp(char *service,int backlog)
{
	return jpassive_sock(service, backlog, "udp");
}

#endif
